#pragma once

#include <memory.h>
#include <glibmm/ustring.h>
#include <assert.h>

#include "Utils.hpp"

namespace BuildNG
{
  template <int on_stack_size>
  class StringBuilder
  {
  public:
    StringBuilder()
    {
      allocated = 0;
      begin = end = on_stack;
      reserved_end = on_stack + on_stack_size;
    }

    ~StringBuilder()
    {
      free(allocated);
    }

    void append(const gchar *str,int len)
    {
      if(end + len > reserved_end)
      {
        size_t cur_reserved = reserved_end - begin;
        size_t cur_size = end - begin;

        if(allocated)
          allocated = (gchar*)realloc(allocated,cur_reserved * 2);
        else
        {
          allocated = (gchar*)malloc(cur_reserved * 2);
          memcpy(allocated,on_stack,cur_size);
        }

        begin = allocated;
        end = allocated + cur_size;
        reserved_end = begin + cur_reserved * 2;
      }

      memcpy(end,str,len);
      end += len;
    }

    void append(const gchar *str)
    {
      int len = (int)strlen(str);
      append(str,len);
    }

    void append(gchar c)
    {
      append(&c,1);
    }

    void append(const gchar *begin,const gchar *end)
    {
      append(begin,(int)(end - begin));
    }

    void append(const StringSegment &segment)
    {
      append(segment.begin,segment.end);
    }

    template <int other_size>
    void append(const StringBuilder<other_size> &str)
    {
      append(str.begin,str.end);
    }

    /// removes the given number of elements from the end of
    /// this stringbuilder.
    void pop_end(int num)
    {
      assert(num <= (int)(end - begin));
      end -= num;
    }

    /// Changes the string, in such a way that all characters,
    /// including and following the given character are removed
    /// from the string.
    void truncate_to(gchar *c)
    {
      assert(c >= begin && c <= end);

      end = c;
    }

    /// Gets a pointer to the beginning of the internal string.
    /// It is allowed to modify the data, between the pointers
    /// returned by this function and \ref get_end.
    gchar* get_begin()
    {
      return begin;
    }

    /// Gets a pointer to the end of the internal string.
    /// It is allowed to modify the data, between the pointers
    /// returned by \ref get_begin and this function.
    gchar* get_end()
    {
      return end;
    }

    const gchar* get_begin() const
    {
      return begin;
    }

    const gchar* get_end() const
    {
      return end;
    }

    int get_size() const
    {
      return (int)(end - begin);
    }

    Glib::ustring to_ustring() const
    {
      return Glib::ustring(begin,end - begin);
    }

    void clear()
    {
      end = begin;
    }

    StringSegment get_string_segment() const
    {
      return StringSegment(begin,end);
    }

    bool validate_utf8()
    {
      return g_utf8_validate(begin,(gssize)(end - begin),NULL);
    }

  private:
    gchar on_stack[on_stack_size];
    gchar *allocated;

    gchar *begin;
    gchar *end;
    gchar *reserved_end;
  };
}
